#include "new_uart.h"
#include "new_gpio.h"

#define TAG "UART"

static const char *string_ch_list[] = {"UART0", "UART1", "UART2", "UART3"};

static uart_rec_cb_t uart_rec_cb[4] = {NULL, NULL, NULL, NULL};
static gpio_t uart_pin_tx[4] = {GPIO_NONE, GPIO_NONE, GPIO_NONE, GPIO_NONE};
static gpio_t uart_pin_rx[4] = {GPIO_NONE, GPIO_NONE, GPIO_NONE, GPIO_NONE};
static uint8_t uart_ready_mask = 0;

void uart_set_rec_cb(uart_ch_t ch, uart_rec_cb_t cb)
{
    uint8_t id = (ch - 0x40003000) / 0x400;
    uart_rec_cb[id] = cb;
}

void uart_init(uart_ch_t ch, uint32_t band, gpio_t txd, gpio_t rxd, uart_rec_cb_t cb)
{
    uint8_t id = (ch - 0x40003000) / 0x400;

    esay_logi(TAG, "%s init band=%d, txd pin=%d, rxd pin=%d", string_ch_list[id], band, txd, rxd);
    uart_pin_tx[id] = txd;
    uart_pin_rx[id] = rxd;

    if (uart_pin_rx[id] != GPIO_NONE)
        gpio_set_mode(rxd, GPIO_MODE_IN_PULLUP);
    if (uart_pin_tx[id] != GPIO_NONE)
    {
        gpio_set_level(txd, 1);
        gpio_set_mode(txd, GPIO_MODE_OUT_PUSHPULL_20mA);
    }

    band = GetSysClock() * 2 / 16 / band;

    // band = 10 * GetSysClock() / 8 / band;
    // band = (band + 5) / 10;
    *(PUINT8V)(ch + 0X0E) = 1;               // FUART = F_SYS * 2 / THIS  -- FUART = F_SYS * 2
    *(PUINT16V)(ch + 0X0C) = (uint16_t)band; // BAND = FUART / 16 / THIS  -- THIS = F_SYS * 2 / 16 / BAND
    *(PUINT8V)(ch + 2) = RB_FCR_TX_FIFO_CLR | RB_FCR_RX_FIFO_CLR | RB_FCR_FIFO_EN;
    *(PUINT8V)(ch + 3) = RB_LCR_WORD_SZ;
    *(PUINT8V)(ch + 1) = RB_IER_TXD_EN | RB_IER_RECV_RDY;

    if (rxd != GPIO_NONE)
    {
        *(PUINT8V)(ch) |= RB_MCR_INT_OE;
        IRQn_Type irq = UART0_IRQn + id;
        if (id >= 2)
            irq = UART2_IRQn + id - 2;
        PFIC_EnableIRQ(irq);
    }
    if (cb != NULL)
        uart_rec_cb[id] = cb;

    uart_ready_mask |= (1 << id);
}

void uart_wait_tx_done(uart_ch_t ch)
{
    while ((*(PUINT8V)(ch + 0x0b)))
        ;
}

void uart_deinit(uart_ch_t ch)
{
    *(PUINT8V)(ch + 1) = RB_IER_RESET;
    uint8_t id = (ch - 0x40003000) / 0x400;

    uart_ready_mask &= ~(1 << id);

    while ((*(PUINT8V)(ch + 0x0b)))
        ;

    if (uart_pin_rx[id] != GPIO_NONE)
    {
        gpio_set_mode(uart_pin_rx[id], GPIO_MODE_IN_FLOAT);
        IRQn_Type irq = UART0_IRQn + id;
        if (id >= 2)
            irq = UART2_IRQn + id - 2;
        PFIC_DisableIRQ(irq);
        uart_pin_rx[id] = GPIO_NONE;
    }
    if (uart_pin_tx[id] != GPIO_NONE)
    {
        gpio_set_mode(uart_pin_tx[id], GPIO_MODE_IN_FLOAT);
        uart_pin_tx[id] = GPIO_NONE;
    }
}

void uart_send(uart_ch_t ch, const uint8_t *data, size_t len)
{
    uint8_t id = (ch - 0x40003000) / 0x400;
    if ((uart_ready_mask & (1 << id)) == 0)
        return;
    switch (ch)
    {
#if UART0_COMPILE_EN
    case UART0:
        UART0_SendString((uint8_t *)data, len);
        break;
#endif

#if UART1_COMPILE_EN
    case UART1:
        UART1_SendString((uint8_t *)data, len);
        break;
#endif

#if UART2_COMPILE_EN
    case UART2:
        UART2_SendString((uint8_t *)data, len);
        break;
#endif

#if UART3_COMPILE_EN
    case UART3:
        UART3_SendString((uint8_t *)data, len);
        break;
#endif

    default:
        break;
    }
}

#if UART0_COMPILE_EN
__INTERRUPT
__HIGH_CODE
void UART0_IRQHandler(void)
{
    volatile char tmp;
    switch (UART0_GetITFlag())
    {
    case UART_II_LINE_STAT: // 线路状态错误
    {
        UART0_GetLinSTA();
        break;
    }

    case UART_II_RECV_RDY: // 数据达到设置触发点
        while (R8_UART0_RFC)
        {
            if (uart_rec_cb[0] != NULL)
                uart_rec_cb[0](tmp);
            break;

        default:
            break;
        }
    }
#endif

#if 1 // UART1_COMPILE_EN
    __INTERRUPT
    __HIGH_CODE
    void UART1_IRQHandler(void)
    {
        volatile char tmp;
        switch (UART1_GetITFlag())
        {
        case UART_II_LINE_STAT: // 线路状态错误
        {
            UART1_GetLinSTA();
            break;
        }

        case UART_II_RECV_RDY: // 数据达到设置触发点
            tmp = UART1_RecvByte();
            if (uart_rec_cb[1] != NULL)
                uart_rec_cb[1](tmp);
            break;

        default:
            break;
        }
    }
#endif

#if UART2_COMPILE_EN
    __INTERRUPT
    __HIGH_CODE
    void UART2_IRQHandler(void)
    {
        volatile char tmp;
        switch (UART2_GetITFlag())
        {
        case UART_II_LINE_STAT: // 线路状态错误
        {
            UART2_GetLinSTA();
            break;
        }

        case UART_II_RECV_RDY: // 数据达到设置触发点
            tmp = R8_UART2_RBR;
            if (uart_rec_cb[2] != NULL)
                uart_rec_cb[2](tmp);
            break;

        default:
            break;
        }
    }
#endif

#if UART3_COMPILE_EN
    __INTERRUPT
    __HIGH_CODE
    void UART3_IRQHandler(void)
    {
        volatile char tmp;
        switch (UART3_GetITFlag())
        {
        case UART_II_LINE_STAT: // 线路状态错误
        {
            UART3_GetLinSTA();
            break;
        }

        case UART_II_RECV_RDY: // 数据达到设置触发点
            tmp = R8_UART3_RBR;
            if (uart_rec_cb[3] != NULL)
                uart_rec_cb[3](tmp);
            break;

        default:
            break;
        }
    }
#endif
